package com.weblog.weblog.web.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.weblog.weblog.common.domain.dos.BlogSettingsDO;
import com.weblog.weblog.common.domain.dos.CommentDO;
import com.weblog.weblog.common.domain.mapper.BlogSettingsMapper;
import com.weblog.weblog.common.domain.mapper.CommentMapper;
import com.weblog.weblog.common.enums.CommentStatusEnum;
import com.weblog.weblog.common.enums.ResponseCodeEnum;
import com.weblog.weblog.common.exception.BizException;
import com.weblog.weblog.common.utils.Response;
import com.weblog.weblog.web.convert.CommentConvert;
import com.weblog.weblog.web.event.PublishCommentEvent;
import com.weblog.weblog.web.model.vo.comment.*;
import com.weblog.weblog.web.service.CommentService;
import com.weblog.weblog.web.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import toolgood.words.IllegalWordsSearch;
import toolgood.words.IllegalWordsSearchResult;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CommentServiceImpl implements CommentService {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private BlogSettingsMapper blogSettingsMapper;
    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    private IllegalWordsSearch wordsSearch;
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /**
     * 根据 QQ 号获取用户信息
     * @author gc
     * @date 2024/3/6 18:29
     * @param findQQUserInfoReqVO
     * @return com.weblog.weblog.common.utils.Response
     */
    @Override
    public Response findQQUserInfo(FindQQUserInfoReqVO findQQUserInfoReqVO) {
        String qq = findQQUserInfoReqVO.getQq();

        //校验
        if(!StringUtil.isPureNumber(qq)){
            log.warn("昵称输入的格式不是 QQ 号: {}",qq);
            throw new BizException(ResponseCodeEnum.NOT_QQ_NUMBER);
        }

        String url = String.format("https://api.qjqq.cn/api/qqinfo?qq=%s", qq);
        String result = restTemplate.getForObject(url,String.class);

        log.info("通过 QQ 号获取用户信息:{}",result);

        //解析响参
        ObjectMapper objectMapper = new ObjectMapper();
        try{
            Map map = objectMapper.readValue(result,Map.class);
            if (Objects.equals(map.get("code"), HttpStatus.OK.value())){
                return Response.success(FindQQUserInfoRspVO.builder()
                        .avatar(String.valueOf(map.get("imgurl")))
                        .nickname(String.valueOf(map.get("name")))
                        .mail(String.valueOf(map.get("mail")))
                        .build());
            }
            return Response.fail();
        }catch (JsonProcessingException e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 发布评论
     * @author gc
     * @date 2024/3/6 19:32
     * @param publishCommentReqVO
     * @return com.weblog.weblog.common.utils.Response
     */

    @Override
    public Response publishComment(PublishCommentReqVO publishCommentReqVO) {
        //回复Id
        Long replyCommentId = publishCommentReqVO.getReplyCommentId();
        //评论内容
        String content = publishCommentReqVO.getContent();
        //昵称
        String nickname = publishCommentReqVO.getNickname();


        BlogSettingsDO blogSettingsDO = blogSettingsMapper.selectById(1L);
        //是否开启敏感词过滤
        Boolean isCommentSensiWordOpen = blogSettingsDO.getIsCommentSensiWordOpen();
        //是否开启审核
        Boolean isCommentExamineOpen = blogSettingsDO.getIsCommentExamineOpen();


        //设置状态
        Integer status = CommentStatusEnum.NORMAL.getCode();
        //审核不通过原因
        String reason = "";

        if(isCommentExamineOpen){
            status = CommentStatusEnum.WAIT_EXAMINE.getCode();
        }

        //是否包含敏感词
        boolean isContainSensitiveWord = false;
        //是否开启敏感词过滤
        if(isCommentSensiWordOpen){
            //校验敏感词
            isContainSensitiveWord = wordsSearch.ContainsAny(content);

            if (isContainSensitiveWord) {
                //包含敏感词
                status = CommentStatusEnum.EXAMINE_FAILED.getCode();
                //匹配到的所有敏感词组
                List<IllegalWordsSearchResult> results = wordsSearch.FindAll(content);
                List<String> keywords = results.stream().map(result -> result.Keyword).collect(Collectors.toList());

                //不通过原因
                reason = String.format("系统自动拦截，包含敏感词：%s",keywords);
                log.warn("此评论内容中包含敏感词: {}, content: {}", keywords, content);
            }
        }

        CommentDO commentDO = CommentDO.builder()
                .avatar(publishCommentReqVO.getAvatar())
                .content(content)
                .mail(publishCommentReqVO.getMail())
                .createTime(LocalDateTime.now())
                .nickname(nickname)
                .routerUrl(publishCommentReqVO.getRouterUrl())
                .website(publishCommentReqVO.getWebsite())
                .replyCommentId(replyCommentId)
                .replyCommentId(publishCommentReqVO.getReplyCommentId())
                .status(status)
                .reason(reason)
                .build();

        commentMapper.insert(commentDO);

        Long commentId = commentDO.getId();

        //发送评论发布事件
        eventPublisher.publishEvent(new PublishCommentEvent(this,commentId));

        if (isContainSensitiveWord)
            throw new BizException(ResponseCodeEnum.COMMENT_CONTAIN_SENSITIVE_WORD);

        if (Objects.equals(status, CommentStatusEnum.WAIT_EXAMINE.getCode()))
            throw new BizException(ResponseCodeEnum.COMMENT_WAIT_EXAMINE);

        return Response.success();
    }

    /**
     * 获取页面所有评论
     * @author gc
     * @date 2024/3/9 11:29
     * @param findCommentListReqVO
     * @return com.weblog.weblog.common.utils.Response
     */

    @Override
    public Response findCommentList(FindCommentListReqVO findCommentListReqVO) {
        //路由地址
        String routerUrl = findCommentListReqVO.getRouterUrl();

        List<CommentDO> commentDOS = commentMapper.selectByRouterUrlAndStatus(routerUrl, CommentStatusEnum.NORMAL.getCode());

        //总评论数
        Integer total =commentDOS.size();

        List<FindCommentItemRspVO> vos = null;
        if (!CollectionUtils.isEmpty(commentDOS)){
            vos = commentDOS.stream()
                    .filter(commentDO -> Objects.isNull(commentDO.getParentCommentId())) // parentCommentId 父级 ID 为空，则表示为一级评论
                    .map(CommentConvert.INSTANCE::convertDO2VO)
                    .collect(Collectors.toList());

            // 循环设置评论回复数据
            vos.forEach(vo -> {
                Long commentId = vo.getId();
                List<FindCommentItemRspVO> childComments = commentDOS.stream()
                        .filter(commentDO -> Objects.equals(commentDO.getParentCommentId(), commentId)) // 过滤出一级评论下所有子评论
                        .sorted(Comparator.comparing(CommentDO::getCreateTime)) // 按发布时间升序排列
                        .map(commentDO -> {
                            FindCommentItemRspVO findPageCommentRspVO = CommentConvert.INSTANCE.convertDO2VO(commentDO);
                            Long replyCommentId = commentDO.getReplyCommentId();
                            // 若二级评论的 replayCommentId 不等于一级评论 ID, 前端则需要展示【回复 @ xxx】，需要设置回复昵称
                            if (!Objects.equals(replyCommentId, commentId)) {
                                // 设置回复用户的昵称
                                Optional<CommentDO> optionalCommentDO = commentDOS.stream()
                                        .filter(commentDO1 -> Objects.equals(commentDO1.getId(), replyCommentId)).findFirst();
                                optionalCommentDO.ifPresent(aDo -> findPageCommentRspVO.setReplyNickname(aDo.getNickname()));
                            }
                            return findPageCommentRspVO;
                        }).collect(Collectors.toList());

                vo.setChildComments(childComments);
            });
        }

        return Response.success(FindCommentListRspVO.builder()
                .total(total)
                .comments(vos)
                .build());
    }
}
